package main

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"time"
	"os"
	"log"
	"sort"
)

const (
	GoServerUrl                      = "http://localhost"
	GoServerPort                     = "8887"
	GoServerConString                = GoServerUrl + ":" + GoServerPort + "/shark"
	SeckillingString                 = "/seckilling"
	QueryUserSeckillingInfoString    = "/queryUserSeckillingInfo"
	QueryProductSeckillingInfoString = "/queryProductSeckillingInfo"
)

var testLogFolder = "../../log/"
var testLogFileName = "shark_test.log"
var testLogFile, _  = os.Create(testLogFolder + testLogFileName)
var testLog = log.New(testLogFile,"",log.Llongfile)

type ShowInfo interface {
	showInfo()
}

// user Request
type UserRequest struct {
	UserId    int "json/userid"
	ProductId int "json/productid"
}

//
// {"errno":1,"status":0,"goodsid":-1}
//
type UserSeckillingInfoJson struct {
	Errno   int "json/errno"
	Status  int "json/status"
	Goodsid int "json/goodsid"
}

type UserGoodsPair struct {
	Userid  int "json/userid"
	Goodsid int "json/goodsid"
}

type QueryProductSeckillingInfoJson struct {
	Error int             "json/error"
	List  []UserGoodsPair "json/list"
}

func (s UserSeckillingInfoJson) showInfo() {
	fmt.Printf("{errno:%d, status:%d, goodsid:%d}\n", s.Errno, s.Status, s.Goodsid)
}

func (q QueryProductSeckillingInfoJson) showInfo() {
	fmt.Printf("{error:%d, list:[", q.Error)
	n := len(q.List)
	for i := 0; i < n; i++ {
		fmt.Printf("{userid:%d, goodsid:%d} ", q.List[i].Userid, q.List[i].Goodsid)
	}
	fmt.Printf("]}\n")
}

//
// 秒杀接口
// http://yourip:port/servicename/seckilling?userid=123456&productid=111
//
func sendRequestseckilling(uid string, pid string) {
	url := GoServerConString + SeckillingString + "?userid=" + uid + "&productid=" + pid

	fmt.Println("request:" + url)

	res, err := http.Get(url)
	if err != nil {
		panic(err.Error())
	}
	defer res.Body.Close()
}

//
// 模仿发送n个随机请求，返回预期的正确结果，用于对比
//
func SendRandomRequest(n int, seed int, reqQueue []UserRequest, resQueue []UserRequest) ([]UserRequest, []UserRequest) {
	pid := 111

	testLog.Println("========== request =========== ")
	rand.Seed(time.Now().UnixNano())

	for i := 0; i < n; i++ {
		uid := rand.Intn(seed)
		reqQueue = append(reqQueue, UserRequest{uid, pid})
		sendRequestseckilling(strconv.Itoa(uid), strconv.Itoa(pid))
		sendRequestQueryUserSeckillingInfo(strconv.Itoa(uid), strconv.Itoa(pid))

		testLog.Printf("request: %d %d\n", uid, pid)
	}

	testLog.Println("============================ ")

	resQueue = sendRequestQueryProductSeckillingInfo(strconv.Itoa(pid), resQueue)

	return reqQueue, resQueue
}

//
// 秒杀结果查询
// http://yourip:port/servicename/queryUserSeckillingInfo?userid=123456&productid=111
//
func sendRequestQueryUserSeckillingInfo(uid string, pid string) {
	url := GoServerConString + QueryUserSeckillingInfoString + "?userid=" + uid + "&productid=" + pid

	fmt.Println("request:" + url)

	res, err := http.Get(url)
	if err != nil {
		panic(err.Error())
	}
	defer res.Body.Close()

	decoder := json.NewDecoder(res.Body)
	var data UserSeckillingInfoJson
	err = decoder.Decode(&data)
	if err != nil {
		fmt.Printf("%T\n%s\n%#v\n", err, err, err)
	}
	data.showInfo()
}

//
// 查询商品秒杀结果
// http://yourip:port/servicename/queryProductSeckillingInfo?productid=111
//
func sendRequestQueryProductSeckillingInfo(pid string, rst []UserRequest) []UserRequest {
	url := GoServerConString + QueryProductSeckillingInfoString + "?productid=" + pid

	fmt.Println("request:" + url)

	res, err := http.Get(url)
	if err != nil {
		panic(err.Error())
	}
	defer res.Body.Close()

	decoder := json.NewDecoder(res.Body)
	var data QueryProductSeckillingInfoJson
	data.List = make([]UserGoodsPair, 0)
	err = decoder.Decode(&data)
	if err != nil {
		fmt.Printf("%T\n%s\n%#v\n", err, err, err)
	}
	data.showInfo()

	testLog.Println("========= result =========== ")
	n := len(data.List)
	v, _ := strconv.Atoi(pid)
	for i := 0; i < n; i++ {
		uid := data.List[i].Userid
		rst = append(rst, UserRequest{uid, v})
		testLog.Printf("result: %d %d\n", uid, v)
	}
	testLog.Println("============================ ")

	return rst
}

type SliceInts []int

func (c SliceInts) Len() int {
    return len(c)
}
func (c SliceInts) Swap(i, j int) {
    c[i], c[j] = c[j], c[i]
}
func (c SliceInts) Less(i, j int) bool {
    return c[i] < c[j]
}

//
// 计算秒杀结果自身重复数目
//
func calDuplicatesNum(q []UserRequest) int {
		var s2 SliceInts = make([]int, 0)

		tot := len(q)
		for i := 0; i < tot; i++ {
			s2 = append(s2, q[i].UserId)
		}

		sort.Sort(s2)

		c := 0
		for i := 1; i < tot; i++ {
			if s2[i] == s2[i-1] {
				c++
			}
		}

		return c
}

//
// 计算正确率：秒杀结果占前n请求的百分比
//
func calMatchError(p []UserRequest, q []UserRequest) float64 {
	var s1 SliceInts = make([]int, 0)
	var s2 SliceInts = make([]int, 0)

	dic := map[int]int{}

	tot := len(p)
	for i := 0; i < tot; i++ {
		if dic[ p[i].UserId ] == 0 {
			dic[ p[i].UserId ] = 1
			s1 = append(s1, p[i].UserId)
		}
	}

	tot = len(q)
	for i := 0; i < tot; i++ {
		s2 = append(s2, q[i].UserId)
	}

	sort.Sort(s2)

	for i := 0; i < tot; i++ {
		dic[ s1[i] ] = 2
	}

	c := 0
	for i := 0; i < tot; i++ {
		if dic[ s2[i] ] == 2 {
			c++
		}
	}

	c -= calDuplicatesNum(q)

	testLog.Printf("check: %d\t%d\n", c, tot)

	return float64(c) * 1.0 / float64(tot);
}

func main() {
	testLog.SetFlags(log.LstdFlags)

	var userRequests []UserRequest
	var queryResults []UserRequest

	userRequests = make([]UserRequest, 0)
	queryResults = make([]UserRequest, 0)

	userRequests, queryResults = SendRandomRequest(1000, 5000, userRequests, queryResults)

	testLog.Printf("成功秒杀总数：%d\n", len(queryResults))
	testLog.Printf("秒杀结果uid重复数： %d\n", calDuplicatesNum(queryResults))
	testLog.Printf("秒杀结果准确率：%.2f%%\n", 100.0*calMatchError(userRequests, queryResults))
}
